/*
* Promise调度器
*   - 允许我们控制Promise的执行顺序，可以用于优化我们的代码性能，尤其是在大型的异步操作中。
*   - Promise调度器允许我们将异步操作分解成多个块，并控制这些块的执行方案。
*   - 与setTimeout和setInterval不同，Promise调度器允许我们将这些异步操作队列化，以便能够更好地管理它们的执行。
 */
class Scheduler {
    constructor(max) {
        // 最大可并发任务数
        this.max = max;
        // 当前并发任务数
        this.count = 0;
        // 阻塞的任务队列
        this.queue = [];
    }

    async add(fn) {
        if (this.count >= this.max) {
            console.log('被堵到这了')
            // 若当前正在执行的任务，达到最大容量max
            // 阻塞在此处，等待前面的任务执行完毕后将resolve弹出并执行
            await new Promise(resolve => this.queue.push(resolve));
            console.log('完成等待')
        }
        // 当前并发任务数++
        this.count++;
        // 使用await执行此函数 这样等待就会出现堵塞现象
        const res = await fn();
        // 执行完毕，当前并发任务数--
        this.count--;
        // 若队列中有值，将其resolve弹出，并执行
        // 以便阻塞的任务，可以正常执行
        this.queue.length && this.queue.shift()();
        // 返回函数执行的结果
        return res;
    }
}

// 延迟函数
const sleep = time => new Promise(resolve => setTimeout(resolve, time));

// 同时进行的任务最多2个
const scheduler = new Scheduler(2);

// 添加异步任务
// time: 任务执行的时间
// val: 参数
const addTask = (time, val) => {
    scheduler.add(() => {
        return sleep(time).then(() => console.log(val));
    });
};

addTask(1000, '1'); // count = 1
addTask(500, '2'); // count = 2
//? 此任务进入达到最大值 会等待前面的任务执行完毕.前面的任务执行完成后会从await fn()那里继续执行 出栈 打印结果
addTask(300, '3');
addTask(400, '4');
// 2
// 3
// 1
// 4
